package cn.lkp.mall.authserver.controller;

import cn.lkp.common.constant.AuthServerConstant;
import cn.lkp.common.expection.BizCodeException;
import cn.lkp.common.utils.R;
import cn.lkp.common.vo.MemberVo;
import cn.lkp.mall.authserver.feign.MemberFeignService;
import cn.lkp.mall.authserver.feign.ThirdPartyFeignService;
import cn.lkp.mall.authserver.vo.RegisterVo;
import cn.lkp.mall.authserver.vo.UserLoginVo;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Controller
public class LoginController {
    @Autowired
    ThirdPartyFeignService thirdPartyFeignService;
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    MemberFeignService memberFeignService;
    /**
     * 发送验证码的处理器方法
     * @param phone
     * @return
     */
    @ResponseBody
    @GetMapping("/sms/send")
    public R sendCode(@RequestParam("phone")String phone){
        //先去redis查相应手机号有无验证码
        String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);
        //如果redis中有之前给相应手机号发过的验证码
        if(!StringUtils.isEmpty(redisCode)){
            //上次发验证码的时间
            long l = Long.parseLong(redisCode.split("_")[1]);
            //当前系统时间（单位毫秒）和上次发验证码的时间间隔不到60s
            if(System.currentTimeMillis()-l < 60000){
                //不能再发,返回错误信息，获取验证码频率过高
                return R.error(BizCodeException.SMS_EXCEPTION.getCode(),BizCodeException.SMS_EXCEPTION.getMsg());
            }
        }
        //TODO 验证码防刷

        //生成code： 6位验证码（随机字符串）
        String code = UUID.randomUUID().toString().substring(0, 5);
        //存储到redis中的redisStorage：code加上系统当前时间（单位毫秒），用_连接
        String redisStorage = code +"_"+System.currentTimeMillis();
        //将上一步的redisStorage存到redis，key: 前缀+phone value: redisStorage 设置10分钟有效期
        redisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX+phone,redisStorage,10, TimeUnit.MINUTES);
        try{
            //远程调用发送验证码的服务，传入手机号和code（6位验证码）
            thirdPartyFeignService.sendSms(phone,code);
        }catch(Exception e){}
        return R.ok();
    }

    /**
     *
     * @param vo
     * @param result
     * @param redirectAttributes 重定向携带数据
     * @return
     */
    @PostMapping("/register")
    public String register(@Valid RegisterVo vo, BindingResult result,
                           RedirectAttributes redirectAttributes){
        if(result.hasErrors()){
            Map<String, String> errors = result.getFieldErrors().stream().collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
            redirectAttributes.addFlashAttribute("errors",errors);
            //用户注册register是post请求，如果注册失败转发到注册页面，而注册页面使用的是路径映射，路径映射默认都是get请求，会报错。这里直接让thymeleaf渲染注册页，不使用转发
            //又因为解决刷新重复提交表单的问题，使用重定向，使用RedirectAttributes代替model携带数据
            //它的本质是使用session,将数据存放在session中，重定向后的页面就能从session中取出数据，但如果再刷新页面或跳转页面，数据就会从session中被删除。
            //检验注册输入信息出错，重定向到注册页面
            return "redirect:http://auth.mall.com/register.html";
        }
        /**
         * 校验验证码
         */
        //根据页面传递过来的手机号，查redis中是否有验证码
        String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone());
        if(!StringUtils.isEmpty(redisCode)){
            //如果页面传递过来的验证码和redis中验证码一致（redis中验证码含系统时间，需截串）
            if(vo.getCode().equals(redisCode.split("_")[0])){
                //校验验证码成功，删除redis中的验证码
                redisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX+vo.getPhone());
                //调用远程服务进行真正的注册
                R r = memberFeignService.register(vo);
                if(r.getCode()==0){
                    //注册成功，重定向到登录页或者首页
                    return "redirect:http://auth.mall.com/login.html";
                }else{
                    Map<String,String> errors = new HashMap<>();
                    errors.put("msg",r.getData("msg",new TypeReference<String>(){}));
                    redirectAttributes.addFlashAttribute("errors",errors);
                    return "redirect:http://auth.mall.com/register.html";
                }
            }else{
                Map<String,String> errors = new HashMap<>();
                errors.put("code","验证码错误");
                redirectAttributes.addFlashAttribute("errors",errors);
                // 重定向到注册页面
                return "redirect:http://auth.mall.com/register.html";
            }
        }else{
            //检验验证码出错,返回错误信息
            Map<String,String> errors = new HashMap<>();
            errors.put("code","验证码错误");
            redirectAttributes.addFlashAttribute("errors",errors);
            // 重定向到注册页面
            return "redirect:http://auth.mall.com/register.html";
        }
    }
    @GetMapping("/login.html")
    public String loginPage(HttpSession session){
        if(session.getAttribute(AuthServerConstant.LOGIN_USER)==null){
            return "login";
        }else{
            return "redirect:http://mall.com";
        }
    }

    @PostMapping("/login")
    public String login(UserLoginVo vo, RedirectAttributes redirectAttributes,
                        HttpSession session){
        //远程调用
        R r = memberFeignService.login(vo);
        if(r.getCode()==0){
            MemberVo data = r.getData("data", new TypeReference<MemberVo>() {
            });
            session.setAttribute(AuthServerConstant.LOGIN_USER,data);
            return "redirect:http://mall.com";
        }else{
            Map<String,String> errors = new HashMap<>();
            errors.put("msg",r.getData("msg",new TypeReference<String>(){}));
            redirectAttributes.addFlashAttribute("erros",errors);
            return "redirect:http://auth.mall.com/login.html";
        }
    }
}
